<?php

declare (strict_types=1);

/**
 * 应用公共函数库文件
 */

use think\facade\Cache;
use think\Response;
use think\facade\Env;
use think\facade\Log;
use think\facade\Config;
use think\facade\Request;
use app\common\library\helper;
use cores\exception\BaseException;
use cores\exception\DebugException;
use think\exception\HttpResponseException;
use Hashids\Hashids;

/**
 * 打印调试函数 html
 * @param $content
 * @param bool $export
 */
function pre($content, bool $export = false)
{
    $output = $export ? var_export($content, true) : print_r($content, true);
    echo "<pre>{$output}</pre>";
    app_end();
}

/**
 * 打印调试函数 json
 * @param $content
 * @param bool $export
 * @throws DebugException
 */
function pree($content, bool $export = false)
{
    $output = $export ? var_export($content, true) : $content;
    throw new DebugException([], $output);
}

/**
 * 输出错误信息
 * @param string $message 报错信息
 * @param int|null $status 状态码,默认为配置文件status.error
 * @param array $data 附加数据
 * @throws BaseException
 */
function throwError(string $message, ?int $status = null, array $data = [])
{
    is_null($status) && $status = config('status.error');
    throw new BaseException(['status' => $status, 'message' => $message, 'data' => $data]);
}

/**
 * 下划线转驼峰
 * @param string $uncamelized_words
 * @param string $separator
 * @return string
 */
function camelize(string $uncamelized_words, string $separator = '_'): string
{
    $uncamelized_words = $separator . str_replace($separator, " ", strtolower($uncamelized_words));
    return ltrim(str_replace(" ", "", ucwords($uncamelized_words)), $separator);
}

/**
 * 驼峰转下划线
 * @param string $camelCaps
 * @param string $separator
 * @return string
 */
function uncamelize(string $camelCaps, string $separator = '_'): string
{
    return strtolower(preg_replace('/([a-z])([A-Z])/', "$1" . $separator . "$2", $camelCaps));
}

/**
 * 生成密码hash值
 * @param string $password
 * @return string
 */
function encryption_hash(string $password): string
{
    return password_hash($password, PASSWORD_DEFAULT);
}

/**
 * 获取当前域名及根路径
 * @return string
 */
function base_url(): string
{
    static $baseUrl = '';
    if (empty($baseUrl)) {
        $request = Request::instance();
        // url协议，设置强制https或自动获取
        $scheme = Config::get('route')['url_force_https'] ? 'https' : $request->scheme();
        // url子目录
        $rootUrl = root_url();
        // 拼接完整url
        $baseUrl = "{$scheme}://" . $request->host() . $rootUrl;
    }
    return $baseUrl;
}

/**
 * 获取当前url的子目录路径
 * @return string
 */
function root_url(): string
{
    static $rootUrl = '';
    if (empty($rootUrl)) {
        $request = Request::instance();
        $subUrl = str_replace('\\', '/', dirname($request->baseFile()));
        $rootUrl = $subUrl . ($subUrl === '/' ? '' : '/');
    }
    return $rootUrl;
}

/**
 * 获取当前uploads目录访问地址
 * @return string
 */
function uploads_url(): string
{
    return base_url() . 'uploads';
}

/**
 * 获取当前temp目录访问地址
 * @return string
 */
function temp_url(): string
{
    return base_url() . 'temp/';
}

/**
 * 获取当前的应用名称
 * @return mixed
 */
function app_name()
{
    return app('http')->getName();
}

/**
 * 获取web根目录
 * @return string
 */
function web_path(): string
{
    static $webPath = '';
    if (empty($webPath)) {
        $request = Request::instance();
        $webPath = dirname($request->server('SCRIPT_FILENAME')) . DIRECTORY_SEPARATOR;
    }
    return $webPath;
}

/**
 * 获取runtime根目录路径
 * @return string
 */
function runtime_root_path(): string
{
    return dirname(runtime_path()) . DIRECTORY_SEPARATOR;
}

/**
 * 写入日志 (使用tp自带驱动记录到runtime目录中)
 * @param $value
 * @param string $type
 */
function log_record($value, string $type = 'info')
{
    $content = is_string($value) ? $value : print_r($value, true);
    Log::record($content, $type);
}

/**
 * 多维数组合并
 * @param array $array1
 * @param array $array2
 * @return array
 */
function array_merge_multiple(array $array1, array $array2): array
{
    $merge = $array1 + $array2;
    $data = [];
    foreach ($merge as $key => $val) {
        if (
            isset($array1[$key])
            && is_array($array1[$key])
            && isset($array2[$key])
            && is_array($array2[$key])
        ) {
            $data[$key] = is_assoc($array1[$key]) ? array_merge_multiple($array1[$key], $array2[$key]) : $array2[$key];
        } else {
            $data[$key] = $array2[$key] ?? $array1[$key];
        }
    }
    return $data;
}

/**
 * 判断是否为自定义索引数组
 * @param array $array
 * @return bool
 */
function is_assoc(array $array): bool
{
    if (empty($array)) return false;
    return array_keys($array) !== range(0, count($array) - 1);
}

/**
 * 二维数组排序
 * @param $arr
 * @param $keys
 * @param bool $desc
 * @return array
 */
function array_sort($arr, $keys, bool $desc = false): array
{
    $key_value = $new_array = array();
    foreach ($arr as $k => $v) {
        $key_value[$k] = $v[$keys];
    }
    if ($desc) {
        arsort($key_value);
    } else {
        asort($key_value);
    }
    reset($key_value);
    foreach ($key_value as $k => $v) {
        $new_array[$k] = $arr[$k];
    }
    return $new_array;
}

/**
 * 隐藏敏感字符
 * @param string $value
 * @return string
 */
function substr_cut(string $value): string
{
    $strlen = mb_strlen($value, 'utf-8');
    if ($strlen <= 1) return $value;
    $firstStr = mb_substr($value, 0, 1, 'utf-8');
    $lastStr = mb_substr($value, -1, 1, 'utf-8');
    return $strlen == 2 ? $firstStr . str_repeat('*', $strlen - 1) : $firstStr . str_repeat("*", $strlen - 2) . $lastStr;
}

/**
 * 获取当前系统版本号
 * @return mixed|null
 * @throws Exception
 */
function get_version()
{
    static $version = [];
    if (!empty($version)) {
        return $version['version'];
    }
    // 读取version.json文件
    $file = root_path() . '/version.json';
    if (!file_exists($file)) {
        throw new Exception('version.json not found');
    }
    // 解析json数据
    $version = helper::jsonDecode(file_get_contents($file));
    if (!is_array($version)) {
        throw new Exception('version cannot be decoded');
    }
    return $version['version'];
}

/**
 * 获取全局唯一标识符
 * @param bool $trim
 * @return string
 */
function get_guid_v4(bool $trim = true): string
{
    // Windows
    if (function_exists('com_create_guid') === true) {
        $charid = com_create_guid();
        return $trim == true ? trim($charid, '{}') : $charid;
    }
    // OSX/Linux
    if (function_exists('openssl_random_pseudo_bytes') === true) {
        $data = openssl_random_pseudo_bytes(16);
        $data[6] = chr(ord($data[6]) & 0x0f | 0x40);    // set version to 0100
        $data[8] = chr(ord($data[8]) & 0x3f | 0x80);    // set bits 6-7 to 10
        return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
    }
    // Fallback (PHP 4.2+)
    mt_srand(intval((double)microtime() * 10000));
    $charid = strtolower(md5(uniqid((string)rand(), true)));
    $hyphen = chr(45);                  // "-"
    $lbrace = $trim ? "" : chr(123);    // "{"
    $rbrace = $trim ? "" : chr(125);    // "}"
    return $lbrace .
        substr($charid, 0, 8) . $hyphen .
        substr($charid, 8, 4) . $hyphen .
        substr($charid, 12, 4) . $hyphen .
        substr($charid, 16, 4) . $hyphen .
        substr($charid, 20, 12) .
        $rbrace;
}

/**
 * 时间戳转换日期
 * @param int|string $timeStamp 时间戳
 * @param bool $withTime 是否关联时间
 * @return false|string
 */
function format_time($timeStamp, bool $withTime = true)
{
    $format = 'Y-m-d';
    $withTime && $format .= ' H:i:s';
    return $timeStamp ? date($format, $timeStamp) : '';
}

/**
 * 左侧填充0
 * @param $value
 * @param int $padLength
 * @return string
 */
function pad_left($value, int $padLength = 2): string
{
    return \str_pad($value, $padLength, "0", STR_PAD_LEFT);
}

/**
 * 重写trim方法 (解决int类型过滤后后变为string类型)
 * @param $str
 * @return mixed
 */
function my_trim($str)
{
    return is_string($str) ? trim($str) : $str;
}

/**
 * 重写htmlspecialchars方法 (解决int类型过滤后后变为string类型)
 * @param $string
 * @return mixed
 */
function my_htmlspecialchars($string)
{
    return is_string($string) ? htmlspecialchars($string, ENT_COMPAT) : $string;
}

/**
 * 过滤emoji表情
 * @param $text
 * @return null|string|string[]
 */
function filter_emoji($text)
{
    if (!is_string($text)) {
        return $text;
    }
    // 此处的preg_replace用于过滤emoji表情
    // 如需支持emoji表情, 需将mysql的编码改为utf8mb4
    return preg_replace('/[\xf0-\xf7].{3}/', '', $text);
}

/**
 * 根据指定长度截取字符串
 * @param $str
 * @param int $length
 * @return bool|string
 */
function str_substr($str, int $length = 30)
{
    if (strlen($str) > $length) {
        $str = mb_substr($str, 0, $length);
    }
    return $str;
}

/**
 * 结束执行
 */
function app_end()
{
    throw new HttpResponseException(Response::create());
}

/**
 * 当前是否为调试模式
 * @return bool
 */
function is_debug(): bool
{
    return (bool)Env::instance()->get('APP_DEBUG');
}

/**
 * 文本左斜杠转换为右斜杠
 * @param string $string
 * @return mixed
 */
function convert_left_slash(string $string)
{
    return str_replace('\\', '/', $string);
}

/**
 * 隐藏手机号中间四位 13012345678 -> 130****5678
 * @param string $mobile 手机号
 * @return string
 */
function hide_mobile(string $mobile): string
{
    return substr_replace($mobile, '****', 3, 4);
}

/**
 * 获取当前登录的商城ID
 * @return int $storeId
 */
function getStoreId(): int
{
    return 10001;
}

/**
 * 创建订单号
 * @return string
 */
// function orderNo(): string
// {
//     return date('Ymd') . substr(implode('', array_map('ord', str_split(substr(uniqid("",true), 7, 13), 1))), 0, 8);
// }

function orderNo($prefix=''): string
{
    return $prefix . date('YmdHis', time()) . substr(microtime(), 2, 6) . sprintf('%03d', rand(0, 999));
}


function responseSucc($data = [], $msg = 'ok', $code = 0):string
{
    if (!headers_sent()) {
        header('Content-type: application/json; charset=utf-8', true);
    }
    $api_end_time = microtime(true);
    $exec_time    = $api_end_time - $GLOBALS['api_start_time'];
    $json_string  = json_encode(array("result" => $code, "exec_time" => $exec_time, "msg" => strval($msg), "data" => $data));
    echo $json_string;
    exit();
}

function responseFail($code = 100, $data = false, $msg = '服务器故障'):string
{
    if (!headers_sent()) {
        header('Content-type: application/json; charset=utf-8', true);
    }
    if (empty($data)) $data = false;
    $json_string = json_encode(array("result" => $code, "msg" => strval($msg), "data" => $data));
    if (isset($_GET['gz'])) {
        echo gzencode($json_string);
        exit();
    }
    echo $json_string;
    exit();
}

//获取递归树形结构
function getTreeListCustom($list, int $parentId = 0,$sub_name = 'children'):array
{
    $data = [];
    foreach ($list as $key => $item) {
        if ($item['parent_id'] == $parentId) {
            $children = getTreeListCustom($list, $item['id']);
            !empty($children) && $item[$sub_name] = $children;
            $data[] = $item;
            unset($list[$key]);
        }
    }
    return $data;
}

function curl_get_https($url,$headers = [])
{
    $ch = curl_init();
    $headers[] = 'Accept-Charset:utf-8';
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $result = curl_exec($ch);
    curl_close($ch);
    return $result;
}


function curl_post_https($url,$data ,$method = 'POST',$header = [])
{
    $curl = curl_init();
    curl_setopt($curl, CURLOPT_URL, $url);
    curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, 0);
    curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, 0);
   // curl_setopt($curl, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']);
    curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
    curl_setopt($curl, CURLOPT_AUTOREFERER, 1);
    if ($method == 'POST') {
        curl_setopt($curl, CURLOPT_POST, 1);
        if ($data != '') {
            curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
            curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
        }
    }
    curl_setopt($curl, CURLOPT_TIMEOUT, 30);
    curl_setopt($curl, CURLOPT_HEADER, 0);
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
    $result = curl_exec($curl);
    curl_close($curl);
    return $result;
}


function get($url){
    $info = curl_init();
    curl_setopt($info,CURLOPT_RETURNTRANSFER,true);
    curl_setopt($info,CURLOPT_HEADER,0);
    curl_setopt($info,CURLOPT_NOBODY,0);
    curl_setopt($info,CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($info,CURLOPT_SSL_VERIFYHOST, false);
    curl_setopt($info,CURLOPT_URL,$url);
    $output= curl_exec($info);
    curl_close($info);
    return $output;
}


//通用post请求方法
function post($url,$data)
{
    $ch = curl_init();
    $params[CURLOPT_URL] = $url;    //请求url地址
    $params[CURLOPT_HEADER] = FALSE; //是否返回响应头信息
    $params[CURLOPT_SSL_VERIFYPEER] = false;
    $params[CURLOPT_SSL_VERIFYHOST] = false;
    $params[CURLOPT_RETURNTRANSFER] = true; //是否将结果返回
    $params[CURLOPT_POST] = true;
    $params[CURLOPT_POSTFIELDS] = $data;
    curl_setopt_array($ch, $params); //传入curl参数
    $content = curl_exec($ch); //执行
    curl_close($ch); //关闭连接
    return $content;
}




/**
 * 数组作分页处理
 */
function doPage($info, $page, $limit): array
{
    $info = array_values((array)$info);
    $start = ($page - 1) * $limit;
    $data['total'] = count($info);
    $data['page'] = $page;
    $data['limit'] = $limit;
    $data['data'] = array_slice($info, $start, $limit);
    //$data['last_page'] = ceil($data['total']/$pageSize);

    $data['per_page'] = $limit;
    $data['current_page'] = $page;
    $data['last_page'] = ceil($data['total']/$limit);

    return $data;
}

function doPageDic($info, $page, $limit): array
{
    $info = array_values((array)$info);
    $start = ($page - 1) * $limit;
    $data['total'] = count($info);
    $data['per_page'] = $limit;
    $data['current_page'] = $page;
    $data['last_page'] = (int)ceil($data['total'] / $limit);
    $data['data'] = array_slice($info, $start, $limit);


    return $data;
}




/**找出数组2不在数组1的元素
 * @param $arr1
 * @param $arr2
 */
function getSurplusData($arr1,$arr2): array
{
    $need_arr = [];
    foreach ($arr2 as $item){
        if(!in_array($item,$arr1)){
            $need_arr[] = $item;
        }
    }
    return $need_arr;
}

/**删除数组中的指定元素
 * @param $arr
 * @param $value
 */
function delByValue($arr , $value): array
{
    $keys = array_keys($arr,$value);
    if(!empty($keys)){
        foreach ($keys as $key){
            unset($arr[$key]);
        }
    }
    return array_values($arr);
}

/**获取所有子ID
 * @param int $pid
 * @param array $data
 * @return array
 */
function getSonIds(int $pid,array $data):array
{
    $arr = [];
    foreach($data as $v){
        if($v['parent_id'] == $pid){
            $arr[] = $v['id'];
            $arr = array_merge($arr,getSonIds($v['id'],$data));
        }
    }
    return $arr;
}

/**
 * 获取当天首尾日期 00:00:00   23:59:59
 */
function getCurrentBetweenTime(): array
{
    $start_time = date('Y-m-d').' 00:00:00';
    $end_time = date('Y-m-d').' 23:59:59';
    return [$start_time,$end_time];
}

/**生成13位时间戳
 * @return
 */
function getUnixTimestamp(): int
{
    list($s1, $s2) = explode(' ', microtime());
    return (int)sprintf('%.0f',(floatval($s1) + floatval($s2)) * 1000);
}


//生成卡序
function createCardNo($length = 20, $prefix = ''): string
{
    $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_';
    $id = '';
    for ($i = 0; $i < $length; $i++) {
        $id .= $characters[rand(0, strlen($characters) - 1)];
    }
    return $prefix.$id;
}

//生成卡密
function signCard($card_no , $salt = 'baize123'): string
{
    $data = [
        'card_no' => $card_no,
        'time' => time()
    ];
    return md5($salt.'&'.http_build_query($data).'&'.$salt);
}


/**获取运营商
 * @param $mobile
 * @return string
 */
function getOperator($mobile): string
{
    $isChinaMobile = "/^134[0-8]\d{7}$|^(?:13[5-9]|147|195|15[0-27-9]|178|18[2-478])\d{8}$/";
    $isChinaUnion = "/^(?:13[0-2]|145|15[56]|175|176|18[56]|198)\d{8}$/";
    $isChinaTelcom = "/^(?:133|153|177|173|18[019]|199|19[1-4])\d{8}$/";
    if (preg_match($isChinaMobile, $mobile)) {
        return '1'; //移动
    } else if (preg_match($isChinaUnion, $mobile)) {
        return '2'; //联通
    } else if (preg_match($isChinaTelcom, $mobile)) {
        return '3'; //电信
    } else {
        return '0'; //未知
    }
}


/**拼接字符串
 * @param array $values
 * @return string
 */
function spliceString(array $values): string
{
    $buff = '';
    foreach ($values as $k => $v) {
        if ($k != 'sign' && $v !== '' && !is_array($v)) {
            $buff .= $k . '=' . $v.'&';
        }
    }
    return trim($buff, '&');
}

/**原理
 * 1、 客户端A请求服务器设置key的值，如果设置成功就表示加锁成功
 * 2、 客户端B也去请求服务器设置key的值，如果返回失败，那么就代表加锁失败
 * 3、 客户端A执行代码完成，删除锁
 * 4、 客户端B在等待一段时间后在去请求设置key的值，设置成功
 * 5、 客户端B执行代码完成，删除锁
 */

/**并发操作原子锁
 * @param $lock_key
 * @param $time
 * @return string[]
 */
function cache_lock($lock_key,$time): array
{
    $cache   = Cache::store('redis');
    $handler = $cache->handler();
    //加锁不成功
    if (!$handler->SET($lock_key, 1, ['NX', 'EX' => $time])){
        throwError('请慢点操作，我反应不过来');
    }
    return [];
}

function getIp(): string
{
    if(!empty($_SERVER["HTTP_CLIENT_IP"]))
    {
        $cip = $_SERVER["HTTP_CLIENT_IP"];
    }
    else if(!empty($_SERVER["HTTP_X_FORWARDED_FOR"]))
    {
        $cip = $_SERVER["HTTP_X_FORWARDED_FOR"];
    }
    else if(!empty($_SERVER["REMOTE_ADDR"]))
    {
        $cip = $_SERVER["REMOTE_ADDR"];
    }
    else
    {
        $cip = '';
    }
    preg_match("/[\d\.]{7,15}/", $cip, $cips);
    $cip = $cips[0] ?? 'unknown';
    unset($cips);
    return $cip;
}



function aes_ecb_encrypt($data, $key = "ZFXEyYaKhxQNAihc" ) {

    try {
        return openssl_encrypt($data, 'aes-128-ecb', $key, 0);
    }catch (\Exception $exception){
        return '';
    }

}

function aes_ecb_decrypt($data,$key = "ZFXEyYaKhxQNAihc")
{
    try {
        return openssl_decrypt($data,'aes-128-ecb', $key, 0);
    }catch (\Exception $exception){
        return '';
    }

}


//生成邀请码
function encodeInviteCode($user_id):string
{
    $hash = new Hashids('fwzz',6);
    return $hash->encode((int)$user_id);
}

//解密邀请码
function decodeInviteCode($hash_str):int
{
    $hash = new Hashids('fwzz',6);
    $arr = $hash->decode($hash_str);
    return $arr[0] ?? 0;
}

